import numpy as np
from  IHSIdividual import HSIndividual
import random
import copy
import time
import matplotlib.pyplot as plt
import math

class ImprovedHarmonySearch:

    '''
    the class for harmony search algorithm
    '''

    def __init__(self, sizepop, vardim, bound, MAXGEN,HMCR,PAR,bw,function):
        '''
        sizepop: population sizepop
        vardim: dimension of variables
        bound: boundaries of variables
        MAXGEN: termination condition

        '''
        self.sizepop = sizepop
        self.vardim = vardim
        self.bound = bound
        self.MAXGEN = MAXGEN

        self.HMCR = HMCR
        self.bw = bw
        self.PAR = PAR
        self.function = function
        self.population = []
        self.fitness = np.zeros((self.sizepop, 1))
        self.trace = []

    def initialize(self):
        '''
        initialize the population of hs
        '''
        for i in range(0, self.sizepop):
            ind = HSIndividual(self.vardim, self.bound,0,self.function)
            ind.generate()
            self.population.append(ind)

    def evaluation(self):
        '''
        evaluation the fitness of the population   求解向量对应的解
        '''
        for i in range(0, self.sizepop):
            self.population[i].calculateFitness()
            self.fitness[i] = self.population[i].fitness

    def improvise(self,PAR,bw):
        '''
        improvise a new harmony     即兴创作
        '''
        ind = HSIndividual(self.vardim, self.bound,0,self.function)


        for i in range(0,self.vardim):
            r1 = random.random()
            r2 = random.random()
            if r1<self.HMCR:
                if r2>PAR:
                    ind.chrom[i]=\
                    self.population[random.randint(0,self.sizepop-1)].chrom[i]
                else:
                    if random.random() > 0.5:
                        ind.chrom[i]= \
                            self.population[random.randint(0, self.sizepop - 1)].chrom[i]+\
                            r2*bw
                    else:
                        ind.chrom[i]= \
                            self.population[random.randint(0, self.sizepop - 1)].chrom[i]-\
                            r2*bw

            else:
                ind.chrom[i] = self.bound[0, i] + \
                                (self.bound[1, i] - self.bound[0, i]) * random.random()

        ind.calculateFitness()
        return ind


    def update(self, ind):
        '''
        update harmony memory
        '''
        # minIdx = np.argmin(self.fitness)
        # if ind.fitness>self.population[minIdx].fitness:
        #     self.population[minIdx] = ind
        #     self.fitness[minIdx] = ind.fitness

        maxIdx = np.argmax(self.fitness)
        if ind.fitness < self.population[maxIdx].fitness:
            #z = self.population[maxIdx].fitness

            self.population[maxIdx] = ind
            self.fitness[maxIdx] = ind.fitness

            #print(z,"update","to",ind.fitness)



    def solve(self):
        '''
        the evolution process of the hs algorithm
        '''
        print("开始执行IHS")
        start = time.time()
        self.t = 0
        self.initialize()
        self.evaluation()

        # best = np.max(self.fitness)
        # bestIndex = np.argmax(self.fitness)

        bestIndex = np.argmin(self.fitness)
        self.best = copy.deepcopy(self.population[bestIndex])
        self.avefitness = np.mean(self.fitness)

        #self.trace[self.t, 0] = (1 - self.best.fitness) / self.best.fitness
        #self.trace[self.t, 1] = (1 - self.avefitness) / self.avefitness

        # self.trace[self.t,0] = self.best.fitness
        # self.trace[self.t,1] = self.avefitness
        # print("Generation %d: optimal function value is: %f; average function value is %f" % (
        #     self.t, self.trace[self.t, 0], self.trace[self.t, 1]))

        worstIndex = np.argmax(self.fitness)
        self.worst = copy.deepcopy(self.population[worstIndex])


        while self.t < self.MAXGEN - 1:
        #while (self.worst.fitness - self.best.fitness) < 1e-10 and self.t > 5000:
            self.t += 1
            PAR = self.PAR[0] + ((self.PAR[1] - self.PAR[0]) / self.MAXGEN) * self.t
            #bw = self.bw[1]-((self.bw[1]-self.bw[0])/self.MAXGEN)*self.t
            c = math.log(self.bw[0]/self.bw[1])/self.MAXGEN
            bw = self.bw[1]*math.exp(c*self.t)
            ind1 = self.improvise(PAR,bw)

            self.update(ind1)


            # best = np.max(self.fitness)
            # bestIndex = np.argmin(self.fitness)
            # if best >self.best.fitness:
            #     self.best = copy.deepcopy(self.population[bestIndex])

            # best = np.min(self.fitness)
            # bestIndex = np.argmin(self.fitness)
            # if best < self.best.fitness:
            #     self.best = copy.deepcopy(self.population[bestIndex])
            # self.avefitness = np.mean(self.fitness)

            # self.trace[self.t, 0] = (1 - self.best.fitness) / self.best.fitness
            # self.trace[self.t, 1] = (1 - self.avefitness) / self.avefitness

            bestIndex = np.argmin(self.fitness)
            self.best = self.population[bestIndex]
            worstIndex = np.argmax(self.fitness)
            self.worst = copy.deepcopy(self.population[worstIndex])
            #self.avefitness = np.mean(self.fitness)

            self.trace.append(self.best.fitness)

        self.var = np.var(self.fitness)
        #print("方差为:",self.var)
        #print("the best chrom")
        #print(self.best.chrom)
        end = time.time()
        print("time:",end-start)
        print("IHSbest:", self.best.fitness)
        print("=================")
